﻿using System;
using System.IO;
using System.Windows.Media;

namespace Gmantis.Imaging
{
    internal class GifInfo
    {
        // Fields
        public int AlphaIndex = -1;
        public byte AspectRatio;
        public byte Background;
        public Color[] ColorTable;
        public int Delay;
        public byte Flags;
        public FrameDisposal FrameDisposal;
        public int Height;
        public bool Interlaced;
        public int Left;
        public Color[] LocalColorTable;
        public int LoopCount;
        public byte LoopFlag;
        public int ScreenHeight;
        public int ScreenWidth;
        public int Top;
        public bool Transparent;
        public int Width;

        // Methods
        internal GifInfo(BinaryReader br)
        {
            this.ScreenWidth = br.ReadUInt16();
            this.ScreenHeight = br.ReadUInt16();
            this.Flags = br.ReadByte();
            this.Background = br.ReadByte();
            this.AspectRatio = br.ReadByte();
            int num = this.Flags & 7;
            bool flag = (this.Flags & 0x80) != 0;
            int num2 = ((int)1) << (num + 1);
            this.ColorTable = new Color[num2];
            if (flag)
            {
                for (int i = 0; i < num2; i++)
                {
                    byte[] buffer = br.ReadBytes(3);
                    this.ColorTable[i] = Color.FromArgb(0xff, buffer[0], buffer[1], buffer[2]);
                }
            }
        }

        private int[] GetInterlacedRows()
        {
            int height = this.Height;
            int[] numArray = new int[height];
            if (!this.Interlaced)
            {
                for (int i = 0; i < height; i++)
                {
                    numArray[i] = i;
                }
                return numArray;
            }
            int index = 0;
            int num4 = 0;
            while (num4 < height)
            {
                numArray[index] = num4;
                num4 += 8;
                index++;
            }
            int num5 = 4;
            while (num5 < height)
            {
                numArray[index] = num5;
                num5 += 8;
                index++;
            }
            int num6 = 2;
            while (num6 < height)
            {
                numArray[index] = num6;
                num6 += 4;
                index++;
            }
            int num7 = 1;
            while (num7 < height)
            {
                numArray[index] = num7;
                num7 += 2;
                index++;
            }
            return numArray;
        }

        private void InflateImageData(BinaryReader br, Bitmap bmp)
        {
            bmp.SetSize(this.Width, this.Height);
            short[] numArray = new short[0x1000];
            byte[] buffer = new byte[0x1000];
            byte[] buffer2 = new byte[0x1001];
            int num = br.ReadByte();
            int num2 = ((int)1) << num;
            int num3 = num2 + 1;
            int index = num2 + 2;
            int num5 = -1;
            int num6 = num + 1;
            int num7 = (((int)1) << num6) - 1;
            for (int i = 0; i < buffer.Length; i++)
            {
                buffer[i] = (byte)i;
            }
            int[] interlacedRows = this.GetInterlacedRows();
            Color[] colorArray = (this.LocalColorTable != null) ? this.LocalColorTable : this.ColorTable;
            byte[] imageData = bmp.GetImageData();
            byte[] buffer4 = null;
            int num9 = 0;
            int num10 = 0;
            int length = 0;
            int num12 = 0;
            int num13 = 0;
            int num14 = 0;
            int num15 = 0;
            int col = 0;
        Label_00BD:
            if (num13 == 0)
            {
                if (num10 < num6)
                {
                    if (length == 0)
                    {
                        buffer4 = this.ReadBlock(br);
                        length = buffer4.Length;
                        if (length == 0)
                        {
                            goto Label_02B9;
                        }
                        num14 = 0;
                    }
                    num9 += (buffer4[num14] & 0xff) << num10;
                    num10 += 8;
                    num14++;
                    length--;
                    goto Label_00BD;
                }
                int num17 = num9 & num7;
                num9 = num9 >> num6;
                num10 -= num6;
                if ((num17 > index) || (num17 == num3))
                {
                    goto Label_02B9;
                }
                if (num17 == num2)
                {
                    num6 = num + 1;
                    num7 = (((int)1) << num6) - 1;
                    index = num2 + 2;
                    num5 = -1;
                    goto Label_00BD;
                }
                if (num5 == -1)
                {
                    buffer2[num13++] = buffer[num17];
                    num5 = num17;
                    num12 = num17;
                    goto Label_00BD;
                }
                int num18 = num17;
                if (num17 == index)
                {
                    buffer2[num13++] = (byte)num12;
                    num17 = num5;
                }
                while (num17 > num2)
                {
                    buffer2[num13++] = buffer[num17];
                    num17 = numArray[num17];
                }
                num12 = buffer[num17] & 0xff;
                if (index >= 0x1000)
                {
                    goto Label_02B9;
                }
                buffer2[num13++] = (byte)num12;
                numArray[index] = (short)num5;
                buffer[index] = (byte)num12;
                index++;
                if (((index & num7) == 0) && (index < 0x1000))
                {
                    num6++;
                    num7 += index;
                }
                num5 = num18;
            }
            int num19 = buffer2[--num13];
            Color color = (this.Transparent && (num19 == this.AlphaIndex)) ? Colors.Transparent : colorArray[num19];
            int pixelIndex = bmp.GetPixelIndex(col, interlacedRows[num15]);
            imageData[pixelIndex] = color.R;
            imageData[pixelIndex + 1] = color.G;
            imageData[pixelIndex + 2] = color.B;
            imageData[pixelIndex + 3] = color.A;
            col++;
            if (col < this.Width)
            {
                goto Label_00BD;
            }
            col = 0;
            num15++;
            if (num15 < this.Height)
            {
                goto Label_00BD;
            }
        Label_02B9:
            while (buffer4.Length > 0)
            {
                buffer4 = this.ReadBlock(br);
            }
        }

        internal void ReadApplicationExtension(BinaryReader br)
        {
            if (br.ReadByte() != 11)
            {
                throw new BadImageFormatException("Invalid Application Extension.");
            }
            br.ReadBytes(8);
            br.ReadBytes(3);
            int num2 = 0;
            while (true)
            {
                int count = br.ReadByte();
                if ((count == 3) && (num2 == 0))
                {
                    this.LoopFlag = br.ReadByte();
                    this.LoopCount = br.ReadUInt16();
                }
                else
                {
                    if (count == 0)
                    {
                        return;
                    }
                    br.ReadBytes(count);
                }
                num2++;
            }
        }

        private byte[] ReadBlock(BinaryReader br)
        {
            byte count = br.ReadByte();
            return br.ReadBytes(count);
        }

        internal void ReadCommentExtension(BinaryReader br)
        {
            while (true)
            {
                int count = br.ReadByte();
                if (count == 0)
                {
                    return;
                }
                br.ReadBytes(count);
            }
        }

        internal void ReadGraphicsControlExtension(BinaryReader br)
        {
            if (br.ReadByte() != 4)
            {
                throw new BadImageFormatException("Invalid Graphics Control Extension.");
            }
            byte num2 = br.ReadByte();
            this.Delay = br.ReadUInt16() * 10;
            this.AlphaIndex = br.ReadByte();
            if (br.ReadByte() != 0)
            {
                throw new BadImageFormatException("Invalid Graphics Control Extension.");
            }
            this.Transparent = (num2 & 1) != 0;
            this.FrameDisposal = ((FrameDisposal)(num2 >> 2)) & ((FrameDisposal)7);
        }

        internal void ReadImageData(BinaryReader br, Bitmap bmp)
        {
            this.Left = br.ReadUInt16();
            this.Top = br.ReadUInt16();
            this.Width = br.ReadUInt16();
            this.Height = br.ReadUInt16();
            byte num = br.ReadByte();
            bool flag = (num & 0x80) != 0;
            this.Interlaced = (num & 0x40) != 0;
            int num2 = num & 7;
            this.LocalColorTable = null;
            if (flag)
            {
                int num3 = ((int)1) << (num2 + 1);
                this.LocalColorTable = new Color[num3];
                for (int i = 0; i < num3; i++)
                {
                    byte[] buffer = br.ReadBytes(3);
                    this.LocalColorTable[i] = Color.FromArgb(0xff, buffer[0], buffer[1], buffer[2]);
                }
            }
            this.InflateImageData(br, bmp);
        }

        internal void ReadPlainTextExtension(BinaryReader br)
        {
            if (br.ReadByte() != 12)
            {
                throw new BadImageFormatException("Invalid Plain Text Extension.");
            }
            br.ReadUInt16();
            br.ReadUInt16();
            br.ReadUInt16();
            br.ReadUInt16();
            br.ReadByte();
            br.ReadByte();
            br.ReadByte();
            br.ReadByte();
            while (true)
            {
                int count = br.ReadByte();
                if (count == 0)
                {
                    return;
                }
                br.ReadBytes(count);
            }
        }
    }
}
